#include "BatchRenderer.h"

namespace SCION_RENDERING
{
  constexpr size_t NUM_SPRITE_VERTICES = 4;
  constexpr size_t NUM_SPRITE_INDICES = 6;
  constexpr size_t MAX_SPRITES = 1000;
  constexpr size_t MAX_INDICES = MAX_SPRITES * NUM_SPRITE_INDICES;
  constexpr size_t MAX_VERTICES = MAX_SPRITES * NUM_SPRITE_VERTICES;

  void BatchRenderer::Initialize()
  {

    glGenVertexArrays(1, &m_VAO);
    glGenBuffers(1, &m_VBO);
    glBindVertexArray(m_VAO);
    // 顶点数据
    glBindBuffer(GL_ARRAY_BUFFER, m_VBO);
    glBufferData(GL_ARRAY_BUFFER, MAX_VERTICES * sizeof(Vertex), nullptr, GL_DYNAMIC_DRAW);
    // ---position ---
    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void *)offsetof(Vertex, position));
    glEnableVertexAttribArray(0);

    // ---uvs ---
    glVertexAttribPointer(
        01, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void *)offsetof(Vertex, uvs));
    glEnableVertexAttribArray(1);

    // --- color ---
    glVertexAttribPointer(
        2, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(Vertex), (void *)offsetof(Vertex, color));
    glEnableVertexAttribArray(2);

    GLuint offset{0};
    GLuint indices[NUM_SPRITE_INDICES] = {
        0, 1, 3, // TL → TR → BR
        0, 3, 2  // TL → BR → BL
    };
    // 绘图可视化 坐标如下：
    //   (-0.5, 0.5) ---- (0.5, 0.5)
    //      |               |
    //      |               |
    //      |               |
    // (-0.5, -0.5) --- (0.5, -0.5)

    auto indicesArr = std::make_unique<GLuint[]>(MAX_INDICES);
    for (size_t i = 0; i < MAX_INDICES; i += NUM_SPRITE_INDICES)
    {
      for (size_t j = 0; j < NUM_SPRITE_INDICES; j++)
      {
        indicesArr[i + j] = indices[j] + offset;
      }
      offset += NUM_SPRITE_VERTICES;
    }

    glGenBuffers(1, &m_IBO);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_IBO);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, MAX_INDICES * sizeof(GLuint), indicesArr.get(), GL_STATIC_DRAW);
    // 解绑
    glBindVertexArray(0);
  }
  void BatchRenderer::GenerateBatches()
  {
    std::vector<Vertex> vertices;
    vertices.reserve(m_Sprites.size() * NUM_SPRITE_VERTICES); // 优化性能

    GLuint offset{0};
    GLuint prevTextureID{0};

    for (const auto &sprite : m_Sprites)
    {
      if (m_Batches.empty() || sprite->textureID != prevTextureID)
      {
        m_Batches.emplace_back(std::make_shared<Batch>(
            Batch{NUM_SPRITE_INDICES, offset, sprite->textureID}));
      }
      else
      {
        m_Batches.back()->nuIndices += NUM_SPRITE_INDICES;
      }

      vertices.push_back(sprite->topLeft);
      vertices.push_back(sprite->topRight);
      vertices.push_back(sprite->bottomRight);
      vertices.push_back(sprite->bottomLeft);

      prevTextureID = sprite->textureID;
      offset += NUM_SPRITE_INDICES;
    }

    // 上传到 GPU
    glBindBuffer(GL_ARRAY_BUFFER, m_VBO);
    glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(Vertex), vertices.data(), GL_DYNAMIC_DRAW);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
  }
  BatchRenderer::BatchRenderer()
      : m_VAO(0), m_VBO(0), m_IBO(0), m_Sprites{}, m_Batches{}
  {
    Initialize();
  }
  BatchRenderer::~BatchRenderer()
  {
    if (m_VAO)
    {
      glDeleteVertexArrays(1, &m_VAO);
    }
    if (m_VBO)
    {
      glDeleteBuffers(1, &m_VBO);
    }
    if (m_IBO)
    {
      glDeleteBuffers(1, &m_IBO);
    }
  }
  void BatchRenderer::Begin()
  {
    m_Sprites.clear();
    m_Batches.clear();
  }
  void BatchRenderer::End()
  {
    if (m_Sprites.empty())
    {
      return;
    }
    std::sort(m_Sprites.begin(), m_Sprites.end(), [&](const auto &a, const auto &b)
              { return a->Layer < b->Layer; });

    GenerateBatches();
  }
  void BatchRenderer::Render()
  {
    if (m_Batches.empty())
    {
      return;
    }
    glBindVertexArray(m_VAO);
    for (const auto &batch : m_Batches)
    {
      glBindTexture(0, batch->textureID);
      glDrawElements(GL_TRIANGLES, batch->nuIndices, GL_UNSIGNED_INT, (void *)(batch->offset * sizeof(GLuint)));
    }
  }
  void BatchRenderer::AddSprite(const glm::vec4 &spriteRect, const glm::vec4 uvRect, GLuint textureID, int layer, glm::mat4 model, const Color &color)
  {
    auto sprite = std::make_shared<Sprite>(
        Sprite{
            Vertex{model * glm::vec4{spriteRect.x, spriteRect.y + spriteRect.w, 0.0f, 1.0f}, glm::vec2{uvRect.x, uvRect.y + uvRect.w}, color},                           // topLeft
            Vertex{model * glm::vec4{spriteRect.x + spriteRect.z, spriteRect.y + spriteRect.w, 0.0f, 1.0f}, glm::vec2{uvRect.x + uvRect.z, uvRect.y + uvRect.w}, color}, // topRight
            Vertex{model * glm::vec4{spriteRect.x, spriteRect.y, 0.0f, 1.0f}, glm::vec2{uvRect.x, uvRect.y}, color},                                                     // bottomLeft
            Vertex{model * glm::vec4{spriteRect.x + spriteRect.z, spriteRect.y, 0.0f, 1.0f}, glm::vec2{uvRect.x + uvRect.z, uvRect.y}, color},                           // bottomRight
            layer,
            textureID});
    m_Sprites.push_back(std::move(sprite));
  }
}
